﻿using System.IO;
using Server;
using System;
using Server.Packets;
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Contracts;

namespace GetThis_Server.Packets
{
    public class S4AuthorizationPacket : Packet
    {
        #region Enumerations

        /// <summary>
        /// Represents the possible status of the authorization.
        /// </summary>
        public enum Status
        {
            /// <summary>
            /// Undefined status.
            /// </summary>
            None = 0,

            /// <summary>
            /// Authorization was accepted.
            /// </summary>
            Accepted = 1,

            /// <summary>
            /// User was not found.
            /// </summary>
            UserNotFound = 2,

            /// <summary>
            /// Key was invalid.
            /// </summary>
            InvalidKey = 3,

            /// <summary>
            /// Ip was invalid.
            /// </summary>
            InvalidIp = 4
        }

        #endregion

        #region Constants

        /// <summary>
        /// Default user name.
        /// </summary>
        private const string DefaultNewKey = "UserKey";

        #endregion

        #region Fields

        /// <summary>
        /// The <see cref="T:GetThis_Server.Packets.S4AuthorizationPacket.Status" /> of the authorization.
        /// </summary>
        private Status m_status;

        /// <summary>
        /// The new key to use after this authorization.
        /// </summary>
        private string m_newKey;

        /// <summary>
        /// The duration of the authorization.
        /// </summary>
        private double m_duration;

        #endregion

        #region Constructors

        /// <summary>
        /// Define the invariant valid state for this class.
        /// </summary>
        [ContractInvariantMethod]
        private void InvariantValidState()
        {
            Contract.Invariant(!String.IsNullOrWhiteSpace(m_newKey));
            Contract.Invariant(m_duration > 0);
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="T:GetThis_Server.Packets.S4AuthorizationPacket" /> class.
        /// </summary>
        public S4AuthorizationPacket()
            : this(Status.None)
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="T:GetThis_Server.Packets.S4AuthorizationPacket" /> class with a given status.
        /// </summary>
        /// <param name="status">The <see cref="T:GetThis_Server.Packets.S4AuthorizationPacket.Status" /> of the authorization.</param>
        public S4AuthorizationPacket(Status status)
            : this(status, DefaultNewKey, 1)
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="T:GetThis_Server.Packets.S4AuthorizationPacket" /> class with a given status and duration.
        /// </summary>
        /// <param name="status">The <see cref="T:GetThis_Server.Packets.S4AuthorizationPacket.Status" /> of the authorization.</param>
        /// <param name="newKey">The new key to use after this authorization.</param>
        /// <param name="duration">The duration of the authorization.</param>
        public S4AuthorizationPacket(Status status, string newKey, double duration)
        {
            Contract.Requires<ArgumentNullException>(!String.IsNullOrWhiteSpace(newKey));
            Contract.Requires<ArgumentOutOfRangeException>(duration > 0);

            m_status = status;
            m_newKey = newKey;
            m_duration = duration;
        }

        #endregion

        #region Properties

        /// <summary>
        /// Identifier.
        /// </summary>
        protected override byte Id
        {
            get { return 4; }
        }

        #endregion

        #region Methods

        /// <summary>
        /// Reads the raw packet data from the data reader.
        /// </summary>
        /// <param name="reader">Data reader.</param>
        /// <exception cref="T:System.IO.EndOfStreamException">The end of the stream is reached.</exception>
        /// <exception cref="T:System.ObjectDisposedException">The stream is closed.</exception>
        /// <exception cref="T:System.IO.IOException">An I/O error occurs.</exception>
        /// <exception cref="T:System.InvalidCastException">The status read was not a defined value for the <see cref="T:GetThis_Server.Packets.S4AuthorizationPacket.Status" /> enumeration.</exception>
        /// <exception cref="T:System.IO.InvalidDataException">The duration read was not a positive number.</exception>
        protected override void ReadPacketData(BinaryReader reader)
        {
            Contract.Ensures(m_duration > 0);

            string status = reader.ReadInt32().ToString();
            if (!Enum.TryParse(status, out m_status))
                throw new InvalidCastException(String.Format("{0} is not a defined value for enum type {1}", status, typeof(Status).FullName));

            var newKey = reader.ReadString();
            if (String.IsNullOrWhiteSpace(newKey))
                throw new InvalidDataException("The new key read was null or empty, or if value consists exclusively of white-space characters.");
            m_newKey = newKey;

            var duration = reader.ReadDouble();
            if (duration <= 0)
                throw new InvalidDataException("The duration read was not a positive number.");
            m_duration = duration;
        }

        /// <summary>
        /// Write the raw packet data to the data writer.
        /// </summary>
        /// <param name="writer">Data writer.</param>
        /// <exception cref="T:System.IO.IOException">An I/O error occurs.</exception>
        /// <exception cref="T:System.ObjectDisposedException">The stream is closed.</exception>
        //[SuppressMessage("Microsoft.Contracts", "CC1055", Justification = "Validation performed in base method")]
        protected override void WritePacketData(BinaryWriter writer)
        {
            Contract.Assert(!String.IsNullOrWhiteSpace(m_newKey));
            Contract.Assert(m_duration > 0);

            writer.Write((int)m_status);
            writer.Write(m_newKey);
            writer.Write(m_duration);
        }

        /// <summary>
        /// Converts this instance to its equivalent string representation.
        /// </summary>
        /// <returns>The string representation of the value of this instance.</returns>
        public override string ToString()
        {
            return String.Format("S4AuthorizationPacket - Status : {0}, NewKey : {1}, Duration : {2}", m_status.ToString(), m_newKey, m_duration);
        }

        #endregion
    }
}
